
#ifndef __LIBERTY_LIBRARY_MAPPEDOBJECT_H__
#define __LIBERTY_LIBRARY_MAPPEDOBJECT_H__

#include <Liberty3D/Liberty3D.h>

namespace Liberty { namespace Library {
   template <typename MutexProvider, typename ObjectType> class MappedObject;

   namespace Mutex { namespace Impl { namespace MappedObject {
        template <typename MutexProvider, typename Base, typename ObjectType>
        class UpgradableLock : public Base {
        public:
            typedef typename MutexProvider::Mutex                             Mutex;
            typedef Liberty::Library::MappedObject<MutexProvider, ObjectType> ObjectBaseType;

        protected:
            ObjectBaseType* _object;

        public:
            UpgradableLock(ObjectBaseType* o) : Base(o->_mutex), _object(o) {
                if (o->mappedObjectNeedsReload()) {
                    typename MutexProvider::UpgradedLock guard(*this);

                    o->mappedObjectLoad();
                }
            }

            UpgradableLock(const ObjectBaseType* o) : Base(o->_mutex), _object(const_cast<ObjectBaseType*>(o)) {
                if (o->mappedObjectNeedsReload()) {
                    typename MutexProvider::UpgradedLock guard(*this);

                    _object->mappedObjectLoad();
                }
            }

            inline ObjectType* object() { return static_cast<ObjectType*>(_object); }
        };

        template <typename MutexProvider, typename Base, typename ObjectType>
        class UpgradedLock : public Base {
        public:
            typedef typename MutexProvider::Mutex                                                     Mutex;
            typedef Liberty::Library::MappedObject<MutexProvider, ObjectType>                         ObjectBaseType;
            typedef UpgradableLock<MutexProvider, typename MutexProvider::UpgradableLock, ObjectType> UpgradableLock;

        protected:
            ObjectBaseType* _object;

        public:
            UpgradedLock(UpgradableLock& upgradableLock) : Base(upgradableLock) {
                _object = upgradableLock.object();

                if (_object->mappedObjectNeedsReload()) {
                    _object->mappedObjectLoad();
                }
            }

            ~UpgradedLock() {
                _object->mappedObjectSave();
            }

            inline ObjectType* object() { return static_cast<ObjectType*>(_object); }
        };

        template <typename MutexProvider, typename Base, typename ObjectType>
        class UniqueLock : public Base {
        public:
            typedef typename MutexProvider::Mutex                             Mutex;
            typedef Liberty::Library::MappedObject<MutexProvider, ObjectType> ObjectBaseType;

        protected:
            ObjectBaseType* _object;

        public:
            UniqueLock(ObjectBaseType* o) : Base(o->mutex), _object(o) {
                if (_object->mappedObjectNeedsReload()) {
                    _object->mappedObjectLoad();
                }
            }

            ~UniqueLock() {
                _object->mappedObjectSave();
            }

            inline ObjectType* object() { return static_cast<ObjectType*>(_object); }
        };
    }}}

    namespace Mutex {
        template <typename MutexProvider, typename ObjectType>
        class MappedObject {
        public:
            typedef typename MutexProvider::Mutex                                                                                                  Mutex;
            typedef Liberty::Library::Mutex::Impl::MappedObject::UpgradableLock<MutexProvider, typename MutexProvider::UpgradableLock, ObjectType> SharedLock;
            typedef Liberty::Library::Mutex::Impl::MappedObject::UpgradableLock<MutexProvider, typename MutexProvider::UpgradableLock, ObjectType> UpgradableLock;
            typedef Liberty::Library::Mutex::Impl::MappedObject::UpgradedLock<MutexProvider,   typename MutexProvider::UpgradedLock,   ObjectType> UpgradedLock;
            typedef Liberty::Library::Mutex::Impl::MappedObject::UniqueLock<MutexProvider,     typename MutexProvider::UniqueLock,     ObjectType> UniqueLock;
        };
    }

    template <typename MutexProvider, typename ObjectType>
    class MappedObject {
        friend class Liberty::Library::Mutex::Impl::MappedObject::UpgradableLock<MutexProvider, typename MutexProvider::UpgradableLock, ObjectType>;
        friend class Liberty::Library::Mutex::Impl::MappedObject::UpgradedLock<MutexProvider,   typename MutexProvider::UpgradedLock,   ObjectType>;
        friend class Liberty::Library::Mutex::Impl::MappedObject::UniqueLock<MutexProvider,     typename MutexProvider::UniqueLock,     ObjectType>;

    public:
        typedef typename MutexProvider::Mutex                                                                                                  Mutex;
        typedef Liberty::Library::Mutex::Impl::MappedObject::UpgradableLock<MutexProvider, typename MutexProvider::UpgradableLock, ObjectType> SharedLock;
        typedef Liberty::Library::Mutex::Impl::MappedObject::UpgradableLock<MutexProvider, typename MutexProvider::UpgradableLock, ObjectType> UpgradableLock;
        typedef Liberty::Library::Mutex::Impl::MappedObject::UpgradedLock<MutexProvider,   typename MutexProvider::UpgradedLock,   ObjectType> UpgradedLock;
        typedef Liberty::Library::Mutex::Impl::MappedObject::UniqueLock<MutexProvider,     typename MutexProvider::UniqueLock,     ObjectType> UniqueLock;

    protected:
        mutable Mutex _mutex;

    public:
        MappedObject() { }
        virtual ~MappedObject() { }

    protected:
        virtual void mappedObjectLoad() = 0;
        virtual void mappedObjectSave() const = 0;
        virtual bool mappedObjectNeedsReload() const = 0;

        template <typename Value>
        void updateValue(Value& v1, const Value& v2) {
            UpgradableLock lock(this);

            if (v1 == v2) {
                return;
            }

            UpgradedLock lockEx(lock);

            v1 = v2;
        }
    };
}}

#endif
